#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Vue.js知识图谱Neo4j导入脚本
使用APOC插件从JSON文件导入数据到远程Neo4j数据库
创建时间: 2025-08-13
"""

import json
import os
from neo4j import GraphDatabase
from typing import Dict, Any

class Neo4jImporter:
    def __init__(self, uri: str, username: str, password: str):
        """
        初始化Neo4j连接
        
        Args:
            uri: Neo4j数据库URI
            username: 用户名
            password: 密码
        """
        self.driver = GraphDatabase.driver(uri, auth=(username, password))
        
    def close(self):
        """关闭数据库连接"""
        if self.driver:
            self.driver.close()
            
    def test_connection(self):
        """测试数据库连接"""
        try:
            with self.driver.session() as session:
                result = session.run("RETURN 'Connection successful' as message")
                record = result.single()
                print(f"✅ 数据库连接成功: {record['message']}")
                return True
        except Exception as e:
            print(f"❌ 数据库连接失败: {e}")
            return False
            
    def check_apoc_plugin(self):
        """检查APOC插件是否可用"""
        try:
            with self.driver.session() as session:
                result = session.run("""
                    CALL apoc.help('load') YIELD name, text
                    WHERE name CONTAINS 'json'
                    RETURN count(*) as apoc_functions
                """)
                record = result.single()
                if record['apoc_functions'] > 0:
                    print(f"✅ APOC插件可用，找到 {record['apoc_functions']} 个JSON相关函数")
                    return True
                else:
                    print("❌ APOC插件不可用或未找到JSON函数")
                    return False
        except Exception as e:
            print(f"❌ 检查APOC插件时出错: {e}")
            return False
            
    def clear_database(self):
        """清空数据库（谨慎使用）"""
        try:
            with self.driver.session() as session:
                session.run("MATCH (n) DETACH DELETE n")
                print("✅ 数据库已清空")
        except Exception as e:
            print(f"❌ 清空数据库时出错: {e}")
            
    def create_constraints_and_indexes(self):
        """创建约束和索引"""
        constraints_and_indexes = [
            # 唯一性约束
            "CREATE CONSTRAINT level_id_unique IF NOT EXISTS FOR (l:Level) REQUIRE l.id IS UNIQUE",
            "CREATE CONSTRAINT topic_id_unique IF NOT EXISTS FOR (t:Topic) REQUIRE t.id IS UNIQUE",
            "CREATE CONSTRAINT subtopic_id_unique IF NOT EXISTS FOR (s:Subtopic) REQUIRE s.id IS UNIQUE",
            "CREATE CONSTRAINT knowledge_point_id_unique IF NOT EXISTS FOR (kp:KnowledgePoint) REQUIRE kp.id IS UNIQUE",
            "CREATE CONSTRAINT concept_id_unique IF NOT EXISTS FOR (c:Concept) REQUIRE c.id IS UNIQUE",
            "CREATE CONSTRAINT resource_id_unique IF NOT EXISTS FOR (r:Resource) REQUIRE r.id IS UNIQUE",
            "CREATE CONSTRAINT assessment_id_unique IF NOT EXISTS FOR (a:Assessment) REQUIRE a.id IS UNIQUE",
            "CREATE CONSTRAINT question_id_unique IF NOT EXISTS FOR (q:Question) REQUIRE q.id IS UNIQUE",
            "CREATE CONSTRAINT path_id_unique IF NOT EXISTS FOR (p:LearningPath) REQUIRE p.id IS UNIQUE",
            
            # 性能索引
            "CREATE INDEX level_order_index IF NOT EXISTS FOR (l:Level) ON (l.order)",
            "CREATE INDEX topic_difficulty_index IF NOT EXISTS FOR (t:Topic) ON (t.difficulty)",
            "CREATE INDEX topic_level_index IF NOT EXISTS FOR (t:Topic) ON (t.level)",
            "CREATE INDEX topic_level_difficulty_index IF NOT EXISTS FOR (t:Topic) ON (t.level, t.difficulty)",
            "CREATE INDEX subtopic_parent_index IF NOT EXISTS FOR (s:Subtopic) ON (s.parent_topic)",
            "CREATE INDEX pathstep_order_index IF NOT EXISTS FOR (ps:PathStep) ON (ps.order)"
        ]
        
        try:
            with self.driver.session() as session:
                for query in constraints_and_indexes:
                    session.run(query)
                print("✅ 约束和索引创建完成")
        except Exception as e:
            print(f"❌ 创建约束和索引时出错: {e}")
            
    def load_json_data(self, json_file_path: str) -> Dict[str, Any]:
        """加载JSON数据"""
        try:
            with open(json_file_path, 'r', encoding='utf-8') as f:
                data = json.load(f)
            print(f"✅ JSON数据加载成功: {json_file_path}")
            return data
        except Exception as e:
            print(f"❌ 加载JSON数据时出错: {e}")
            return {}
            
    def import_data_directly(self, data: Dict[str, Any]):
        """直接使用Python数据导入（不依赖APOC文件加载）"""
        try:
            with self.driver.session() as session:
                # 导入层级数据
                if 'levels' in data:
                    for level in data['levels']:
                        session.run("""
                            CREATE (l:Level {
                                id: $id,
                                name: $name,
                                order: $order,
                                description: $description,
                                color: $color
                            })
                        """, level)
                    print(f"✅ 导入了 {len(data['levels'])} 个层级")
                
                # 导入知识点数据
                if 'topics' in data:
                    for topic in data['topics']:
                        session.run("""
                            CREATE (t:Topic {
                                id: $id,
                                name: $name,
                                level: $level,
                                difficulty: $difficulty,
                                description: $description,
                                estimated_hours: $estimated_hours
                            })
                        """, {
                            'id': topic['id'],
                            'name': topic['name'],
                            'level': topic['level'],
                            'difficulty': topic['difficulty'],
                            'description': topic['description'],
                            'estimated_hours': topic['estimated_hours']
                        })
                    print(f"✅ 导入了 {len(data['topics'])} 个知识点")
                
                # BELONGS_TO关系现在通过relationships数组统一处理
                
                # REQUIRES关系现在通过relationships数组统一处理
                
                # 导入子知识点
                if 'subtopics' in data:
                    subtopic_count = 0
                    for subtopic in data['subtopics']:
                        # 从subtopic的id中提取parent_topic
                        parent_topic = None
                        for topic in data.get('topics', []):
                            if subtopic['id'].startswith(topic['id'] + '-'):
                                parent_topic = topic['id']
                                break
                        
                        session.run("""
                            CREATE (s:Subtopic {
                                id: $id,
                                name: $name,
                                description: $description,
                                parent_topic: $parent_topic,
                                difficulty: $difficulty
                            })
                        """, {
                            'id': subtopic['id'],
                            'name': subtopic['name'],
                            'description': subtopic.get('description', ''),
                            'parent_topic': parent_topic,
                            'difficulty': subtopic.get('difficulty', 'medium')
                        })
                        subtopic_count += 1
                    print(f"✅ 导入了 {subtopic_count} 个子知识点")
                
                # 导入知识点
                if 'knowledge_points' in data:
                    kp_count = 0
                    for kp in data['knowledge_points']:
                        session.run("""
                            CREATE (kp:KnowledgePoint {
                                id: $id,
                                name: $name,
                                parent_subtopic: $parent_subtopic,
                                content: $content,
                                key_points: $key_points,
                                examples: $examples,
                                difficulty: $difficulty
                            })
                        """, {
                            'id': kp['id'],
                            'name': kp['name'],
                            'parent_subtopic': kp.get('parent_subtopic'),
                            'content': kp.get('content', ''),
                            'key_points': kp.get('key_points', []),
                            'examples': kp.get('examples', []),
                            'difficulty': kp.get('difficulty', 'medium')
                        })
                        kp_count += 1
                    print(f"✅ 导入了 {kp_count} 个知识点")
                
                # 导入概念
                if 'concepts' in data:
                    concept_count = 0
                    for concept in data['concepts']:
                        session.run("""
                            CREATE (c:Concept {
                                id: $id,
                                name: $name,
                                parent_knowledge_point: $parent_knowledge_point,
                                definition: $definition,
                                examples: $examples,
                                related_concepts: $related_concepts
                            })
                        """, {
                            'id': concept['id'],
                            'name': concept['name'],
                            'parent_knowledge_point': concept.get('parent_knowledge_point'),
                            'definition': concept.get('definition', ''),
                            'examples': concept.get('examples', []),
                            'related_concepts': concept.get('related_concepts', [])
                        })
                        concept_count += 1
                    print(f"✅ 导入了 {concept_count} 个概念")
                
                # 导入学习资源
                if 'topics' in data:
                    resource_count = 0
                    for topic in data['topics']:
                        if 'resources' in topic and topic['resources']:
                            for resource in topic['resources']:
                                resource_id = f"{topic['id']}_{resource['type']}"
                                session.run("""
                                    CREATE (r:Resource {
                                        id: $id,
                                        type: $type,
                                        title: $title,
                                        path: $path,
                                        section: $section
                                    })
                                """, {
                                    'id': resource_id,
                                    'type': resource['type'],
                                    'title': resource['title'],
                                    'path': resource['path'],
                                    'section': resource['section']
                                })
                                
                                session.run("""
                                    MATCH (t:Topic {id: $topic_id})
                                    MATCH (r:Resource {id: $resource_id})
                                    CREATE (t)-[:HAS_RESOURCE]->(r)
                                """, {
                                    'topic_id': topic['id'],
                                    'resource_id': resource_id
                                })
                                resource_count += 1
                    print(f"✅ 导入了 {resource_count} 个学习资源")
                
                # 导入Quiz节点
                if 'quizzes' in data:
                    quiz_count = 0
                    for quiz in data['quizzes']:
                        session.run("""
                            CREATE (q:Quiz {
                                id: $id,
                                topicId: $topicId,
                                title: $title,
                                description: $description,
                                passingScore: $passingScore,
                                timeLimit: $timeLimit,
                                maxAttempts: $maxAttempts,
                                difficulty: $difficulty,
                                tags: $tags
                            })
                        """, {
                            'id': quiz['id'],
                            'topicId': quiz['topicId'],
                            'title': quiz['title'],
                            'description': quiz['description'],
                            'passingScore': quiz['passingScore'],
                            'timeLimit': quiz['timeLimit'],
                            'maxAttempts': quiz['maxAttempts'],
                            'difficulty': quiz['difficulty'],
                            'tags': quiz['tags']
                        })
                        
                        # HAS_QUIZ关系现在通过relationships数组统一处理
                        quiz_count += 1
                    print(f"✅ 导入了 {quiz_count} 个测验")
                
                # 导入Student节点
                if 'students' in data:
                    student_count = 0
                    for student in data['students']:
                        session.run("""
                            CREATE (s:Student {
                                id: $id,
                                name: $name,
                                email: $email,
                                level: $level,
                                registeredAt: $registeredAt,
                                lastActiveAt: $lastActiveAt,
                                totalStudyHours: $totalStudyHours,
                                completedTopics: $completedTopics,
                                currentTopic: $currentTopic,
                                learningPath: $learningPath
                            })
                        """, {
                            'id': student['id'],
                            'name': student['name'],
                            'email': student['email'],
                            'level': student['level'],
                            'registeredAt': student['registeredAt'],
                            'lastActiveAt': student['lastActiveAt'],
                            'totalStudyHours': student['totalStudyHours'],
                            'completedTopics': student['completedTopics'],
                            'currentTopic': student['currentTopic'],
                            'learningPath': student['learningPath']
                        })
                        student_count += 1
                    print(f"✅ 导入了 {student_count} 个学生")
                
                # 导入评估测试
                if 'assessments' in data:
                    assessment_count = 0
                    for assessment in data['assessments']:
                        # 检查是否是PathStep类型
                        if assessment.get('type') == 'path_step':
                            session.run("""
                                CREATE (ps:PathStep {
                                    id: $id,
                                    pathId: $pathId,
                                    topicId: $topicId,
                                    order: $order,
                                    title: $title,
                                    description: $description,
                                    status: $status
                                })
                            """, {
                                'id': assessment['id'],
                                'pathId': assessment['pathId'],
                                'topicId': assessment['topicId'],
                                'order': assessment['order'],
                                'title': assessment['title'],
                                'description': assessment['description'],
                                'status': assessment['status']
                            })
                        else:
                            # 原有的Assessment逻辑
                            assessment_id = f"{assessment.get('topic_id', assessment.get('topicId', 'unknown'))}_assessment"
                            session.run("""
                                CREATE (a:Assessment {
                                    id: $id,
                                    topic_id: $topic_id,
                                    type: $type,
                                    difficulty: $difficulty,
                                    passing_score: $passing_score
                                })
                            """, {
                                'id': assessment_id,
                                'topic_id': assessment.get('topic_id', assessment.get('topicId')),
                                'type': assessment.get('type', 'quiz'),
                                'difficulty': assessment.get('difficulty', 'medium'),
                                'passing_score': assessment.get('passing_score', 70)
                            })
                        assessment_count += 1
                    print(f"✅ 导入了 {assessment_count} 个评估项目")
                
                # 导入学习路径
                if 'learning_paths' in data:
                    path_count = 0
                    for path in data['learning_paths']:
                        session.run("""
                            CREATE (p:LearningPath {
                                id: $id,
                                name: $name,
                                description: $description,
                                difficulty: $difficulty,
                                estimated_total_hours: $estimated_total_hours,
                                prerequisites: $prerequisites,
                                tags: $tags
                            })
                        """, {
                            'id': path['id'],
                            'name': path['name'],
                            'description': path['description'],
                            'difficulty': path.get('difficulty', 'beginner'),
                            'estimated_total_hours': path['estimated_total_hours'],
                            'prerequisites': path.get('prerequisites', []),
                            'tags': path.get('tags', [])
                        })
                        path_count += 1
                    print(f"✅ 导入了 {path_count} 个学习路径")
                
                # 导入所有关系
                if 'relationships' in data:
                    relationship_count = 0
                    for rel in data['relationships']:
                        rel_type = rel['type']
                        from_label = rel['fromLabel']
                        to_label = rel['toLabel']
                        from_id = rel['from']
                        to_id = rel['to']
                        properties = rel.get('properties', {})
                        
                        # 构建属性字符串
                        prop_str = ""
                        prop_params = {'from_id': from_id, 'to_id': to_id}
                        
                        if properties:
                            prop_items = []
                            for key, value in properties.items():
                                prop_items.append(f"{key}: ${key}")
                                prop_params[key] = value
                            prop_str = f" {{{', '.join(prop_items)}}}"
                        
                        query = f"""
                            MATCH (from:{from_label} {{id: $from_id}})
                            MATCH (to:{to_label} {{id: $to_id}})
                            CREATE (from)-[:{rel_type}{prop_str}]->(to)
                        """
                        
                        try:
                            result = session.run(query, prop_params)
                            relationship_count += 1
                            print(f"✅ 创建关系: {rel_type} from {from_id} to {to_id}")
                        except Exception as e:
                            print(f"⚠️ 关系创建失败: {rel_type} from {from_id} to {to_id}, 错误: {str(e)}")
                            # 检查节点是否存在
                            from_check = session.run(f"MATCH (n:{from_label} {{id: $id}}) RETURN count(n) as count", {'id': from_id})
                            to_check = session.run(f"MATCH (n:{to_label} {{id: $id}}) RETURN count(n) as count", {'id': to_id})
                            from_exists = from_check.single()['count'] > 0
                            to_exists = to_check.single()['count'] > 0
                            print(f"   From节点存在: {from_exists}, To节点存在: {to_exists}")
                    
                    print(f"✅ 导入了 {relationship_count} 个关系")
                
                # 注意：学习路径步骤和关系现在通过assessments和relationships数组统一处理
                # 不再需要单独的学习路径步骤创建逻辑，避免重复创建关系
                
                print("✅ 所有数据导入完成")
                
        except Exception as e:
            print(f"❌ 导入数据时出错: {e}")
            
    def validate_import(self):
        """验证导入结果"""
        try:
            with self.driver.session() as session:
                # 统计各类型节点数量
                result = session.run("""
                    CALL {
                        MATCH (n:Level) RETURN 'Level' as type, count(n) as count
                        UNION
                        MATCH (n:Topic) RETURN 'Topic' as type, count(n) as count
                        UNION
                        MATCH (n:Subtopic) RETURN 'Subtopic' as type, count(n) as count
                        UNION
                        MATCH (n:KnowledgePoint) RETURN 'KnowledgePoint' as type, count(n) as count
                        UNION
                        MATCH (n:Concept) RETURN 'Concept' as type, count(n) as count
                        UNION
                        MATCH (n:Quiz) RETURN 'Quiz' as type, count(n) as count
                        UNION
                        MATCH (n:Student) RETURN 'Student' as type, count(n) as count
                        UNION
                        MATCH (n:Assessment) RETURN 'Assessment' as type, count(n) as count
                        UNION
                        MATCH (n:LearningPath) RETURN 'LearningPath' as type, count(n) as count
                        UNION
                        MATCH (n:Resource) RETURN 'Resource' as type, count(n) as count
                        UNION
                        MATCH (n:PathStep) RETURN 'PathStep' as type, count(n) as count
                    }
                    RETURN type, count
                    ORDER BY type
                """)
                
                print("\n📊 节点统计:")
                for record in result:
                    print(f"  {record['type']}: {record['count']}")
                
                # 统计关系数量
                result = session.run("""
                    CALL {
                        MATCH ()-[r:BELONGS_TO]->() RETURN 'BELONGS_TO' as type, count(r) as count
                        UNION
                        MATCH ()-[r:PART_OF]->() RETURN 'PART_OF' as type, count(r) as count
                        UNION
                        MATCH ()-[r:HAS_QUIZ]->() RETURN 'HAS_QUIZ' as type, count(r) as count
                        UNION
                        MATCH ()-[r:HAS_STEP]->() RETURN 'HAS_STEP' as type, count(r) as count
                        UNION
                        MATCH ()-[r:REQUIRES]->() RETURN 'REQUIRES' as type, count(r) as count
                        UNION
                        MATCH ()-[r:HAS_RESOURCE]->() RETURN 'HAS_RESOURCE' as type, count(r) as count
                    }
                    RETURN type, count
                    ORDER BY type
                """)
                
                print("\n🔗 关系统计:")
                for record in result:
                    print(f"  {record['type']}: {record['count']}")
                
                # 显示四层架构结构
                result = session.run("""
                    MATCH (l:Level)
                    OPTIONAL MATCH (l)<-[:BELONGS_TO]-(t:Topic)
                    OPTIONAL MATCH (t)<-[:PART_OF]-(s:Subtopic)
                    OPTIONAL MATCH (s)<-[:PART_OF]-(kp:KnowledgePoint)
                    OPTIONAL MATCH (kp)<-[:PART_OF]-(c:Concept)
                    RETURN l.name as 层级, l.order as 顺序, 
                           count(DISTINCT t) as 主题数量,
                           count(DISTINCT s) as 子主题数量,
                           count(DISTINCT kp) as 知识点数量,
                           count(DISTINCT c) as 概念数量
                    ORDER BY l.order
                """)
                print("\n📚 四层架构统计:")
                for record in result:
                    print(f"  {record['层级']} (顺序{record['顺序']}): {record['主题数量']}个主题, {record['子主题数量']}个子主题, {record['知识点数量']}个知识点, {record['概念数量']}个概念")
                    
        except Exception as e:
            print(f"❌ 验证导入结果时出错: {e}")

def main():
    """主函数"""
    # Neo4j连接配置
    NEO4J_URI = "neo4j://49.235.155.102:7687"
    NEO4J_USERNAME = "neo4j"
    NEO4J_PASSWORD = "123qweasd!"
    
    # JSON文件路径
    JSON_FILE_PATH = "generated-vue-knowledge-graph.json"
    
    # 创建导入器实例
    importer = Neo4jImporter(NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD)
    
    try:
        print("🚀 开始Vue.js知识图谱导入流程...\n")
        
        # 1. 测试连接
        print("1️⃣ 测试数据库连接...")
        if not importer.test_connection():
            return
        
        # 2. 检查APOC插件
        print("\n2️⃣ 检查APOC插件...")
        if not importer.check_apoc_plugin():
            print("⚠️ APOC插件不可用，将使用直接导入方式")
        
        # 3. 询问是否清空数据库
        clear_db = input("\n3️⃣ 是否清空现有数据库？(y/N): ").lower().strip()
        if clear_db == 'y':
            importer.clear_database()
        
        # 4. 创建约束和索引
        print("\n4️⃣ 创建约束和索引...")
        importer.create_constraints_and_indexes()
        
        # 5. 加载JSON数据
        print("\n5️⃣ 加载JSON数据...")
        if not os.path.exists(JSON_FILE_PATH):
            print(f"❌ JSON文件不存在: {JSON_FILE_PATH}")
            return
        
        data = importer.load_json_data(JSON_FILE_PATH)
        if not data:
            return
        
        # 6. 导入数据
        print("\n6️⃣ 导入数据...")
        importer.import_data_directly(data)
        
        # 7. 验证导入结果
        print("\n7️⃣ 验证导入结果...")
        importer.validate_import()
        
        print("\n🎉 Vue.js知识图谱导入完成！")
        print(f"\n🌐 您可以通过以下方式访问Neo4j浏览器:")
        print(f"   URL: http://49.235.155.102:8081/")
        print(f"   连接URI: {NEO4J_URI}")
        print(f"   用户名: {NEO4J_USERNAME}")
        
    except KeyboardInterrupt:
        print("\n⏹️ 用户中断操作")
    except Exception as e:
        print(f"\n❌ 导入过程中发生错误: {e}")
    finally:
        # 关闭连接
        importer.close()
        print("\n🔌 数据库连接已关闭")

if __name__ == "__main__":
    main()